<?php
/**
 * Created by PhpStorm.
 * User: 牛赞赞
 * Date: 2021/1/29
 * Time: 18:58
 */

namespace App\Task;

use App\Game\Core\Packet;
use App\Game\Conf\MainCmd;
use App\Game\Conf\SubCmd;
use App\Model\User;
use Psr\Container\ContainerInterface;
use Swoole\WebSocket\Server as WebSocketServer;

class RobotJoinTimerTask
{
    /**
     * @var ContainerInterface
     */
    private $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    /**
     * 加入房间计时器
     * @param $account
     * @param $room_no
     */
    public function robotJoinTimer($account,$room_no){
        $robot_conf = config('robot');
        $game_conf  = config('game');
        $serv       = server();
        $room_wait_key      = sprintf($game_conf['user_room_wait'],$room_no);
        sleep($robot_conf['wait_join_timer']);
        $loop_num = $this->getRobotLoop($room_no);
        if($account == $loop_num){
            // 计时器触发
            if(redis()->exists($room_wait_key)){
                $this->addUserRoomMap($account,$room_no);      // 绑定用户和房间号
                $this->addRoomUser($account,$room_no);         // 将新加入用户推入等待房间
                $this->addRoomChair($account,$room_no);         // 将新加入用户推入等待房间
                $this->addPrepare($account,$room_no);           // 将机器人推入准备
                $players = $this->checkWaitMembers($room_no,$account);  //检查房间人数
                $match_players = $this->matchChair($players['players'],$room_no);
                $room_data = ['status'=>'waiting','players'=>$match_players,'room_no'=>$room_no];
                $fds = $this->getRoomFds($game_conf,$room_wait_key);// 获取房间所有用户fd
                $this->roomBroadcast($serv,$room_data,$fds);//向房间人广播
                $this->worldBroadcast($serv,$fds);// 向世界广播

                // 准备成功通知
                $prepare_chairs = $this->getPrepareChairs($room_no);
                $msg = ['status'=>'通知成功','chair_id'=>$prepare_chairs];
                $this->roomPrepareBroadcast($serv,$fds,$msg);

                // 查询房间等待人数
                $wait_users = json_decode(redis()->get($room_wait_key));
                if((count($wait_users) >= 3) && (count($prepare_chairs) >= 3)){
                    //投递异步发牌任务
                    $task = container()->get(GameSyncTask::class);
                    $task->assignCards($room_no);

                    // 清除计数
                    $this->cleanRobotLoop($room_no);
                }else{
                    // 投递下一次机器人
                    $task = container()->get(RobotAssignTask::class);
                    $task->assignRobot($room_no);
                }
            }
        }
    }

    /**
     * 获取计时器计数
     * @param $room_no
     * @return bool|mixed|null|string
     */
    protected function getRobotLoop($room_no){
        $robot_conf = config('robot');
        $robot_loop_key = sprintf($robot_conf['user_room_robot'],$room_no);
        if(redis()->exists($robot_loop_key)){
            return redis()->get($robot_loop_key);
        }else{
            return null;
        }
    }

    /**
     * 清除机器人计数
     * @param $room_no
     */
    protected function cleanRobotLoop($room_no){
        $robot_conf = config('robot');
        $robot_loop_key = sprintf($robot_conf['user_room_robot'],$room_no);
        redis()->del($robot_loop_key);
    }

    /**
     * 绑定用户和房间
     * @param $account
     * @param $room_no
     */
    public function addUserRoomMap($account,$room_no){
        $game_conf  = config('game');
        $room_map_conf = $game_conf['user_room'];
        $room_map_key  = sprintf($room_map_conf,$account);
        redis()->set($room_map_key,(int)$room_no);// 绑定用户房间号
    }

    /**
     * 绑定房间和用户
     * @param $account
     * @param $room_no
     */
    public function addRoomUser($account,$room_no){
        $game_conf  = config('game');
        $room_wait_conf = $game_conf['user_room_wait'];
        $room_wait_key  = sprintf($room_wait_conf,$room_no);
        $json_wait_room = redis()->get($room_wait_key);
        $arr_wait_room = [];
        if($json_wait_room){
            $arr_wait_room = json_decode($json_wait_room,true);
            if(!in_array($account,$arr_wait_room)){
                array_push($arr_wait_room,$account);
            }
        }
        redis()->set($room_wait_key,json_encode($arr_wait_room));// 绑定用户房间号
    }

    /**
     * 绑定桌位号
     * @param $account
     * @param $room_no
     */
    public function addRoomChair($account,$room_no){
        $game_conf  = config('game');
        $room_chair_key  = sprintf($game_conf['user_room_chair'],$room_no);
        $room_chair_data = [];
        if(redis()->exists($room_chair_key)){
            $room_chair_data = json_decode(redis()->get($room_chair_key),true);
            if(!in_array(1,$room_chair_data)){
                $room_chair_data[$account] = 1;
            }elseif(!in_array(2,$room_chair_data)){
                $room_chair_data[$account] = 2;
            }elseif(!in_array(3,$room_chair_data)){
                $room_chair_data[$account] = 3;
            }
        }else{
            $room_chair_data[$account] = 1;
        }
        redis()->set($room_chair_key,json_encode($room_chair_data));// 绑定用户房间号桌位号
    }

    /**
     * 绑定准备
     * @param $account
     * @param $room_no
     */
    public function addPrepare($account,$room_no){
        $game_conf  = config('game');
        $room_prepare_key = sprintf($game_conf['user_room_prepare'],$room_no);
        if(redis()->exists($room_prepare_key)){
            $cache_json = redis()->get($room_prepare_key);
            $cache_arr = json_decode($cache_json,true);
            array_push($cache_arr,$account);
        }else{
            $cache_arr = [$account];
        }
        if(count($cache_arr) <= 3){
            redis()->set($room_prepare_key,json_encode($cache_arr));
        }
    }

    /**
     * 匹配玩家和桌位号
     * @param $players
     * @param $room_no
     * @return mixed
     */
    public function matchChair($players,$room_no){
        $game_conf  = config('game');
        $room_chair_key  = sprintf($game_conf['user_room_chair'],$room_no);
        $room_chair_data = json_decode(redis()->get($room_chair_key),true);
        foreach ($players as $k=>$v){
            $players[$k]['chair_id'] = $room_chair_data[$v['account']];
        }
        return $players;
    }

    /**
     * 获取房间所有用户fd
     * @param $game_conf
     * @param $room_wait_key
     * @return array
     */
    protected function getRoomFds($game_conf,$room_wait_key){
        $room_users    = json_decode(redis()->get($room_wait_key),true);
        $fds = [];
        foreach ($room_users as $k=>$v){
            $bind_key = sprintf($game_conf['user_bind_key'],$v);
            $fds[] = redis()->get($bind_key);
        }
        return $fds;
    }

    /**
     * 检查等待房间人数，不算自身
     * @param $room_no
     * @param $account
     * @return array
     */
    protected function checkWaitMembers($room_no,$account){
        $game_conf      = config('game');
        $wait_room_key  = sprintf($game_conf['user_room_wait'],$room_no);
        $msg = [];
        if(redis()->exists($wait_room_key)){
            $player = json_decode(redis()->get($wait_room_key),true);
            if(in_array($account,$player)){
                $diff_player = array_diff($player,[$account]);
                array_push($player,$account);
                $msg['len']     = count($diff_player);
                $msg['in_room'] = 1;
            }else{
                $msg['len'] = count($player);
                $msg['in_room'] = 0;
            }
            if(!empty($player)){
                $msg['players'] = $this->membersInfo($player);
            }else{
                $msg['players'] = [];
            }
        }
        return $msg;
    }

    /**
     *
     * 查询房间用户信息
     * @param $userArr
     * @return array
     */
    protected function membersInfo($userArr){
        if(is_array($userArr) && !empty($userArr)){
            $users = User::getUsers($userArr);
            if($users['code'] == 0){
                return $users['data'];
            }
        }
        return [];
    }

    /**
     * 返回玩家房间桌位号
     * @param $room_no
     * @return mixed
     */
    protected function getPrepareChairs($room_no){
        $game_conf = config('game');
        $room_chair_key   = sprintf($game_conf['user_room_chair'],$room_no);
        $room_prepare_key = sprintf($game_conf['user_room_prepare'],$room_no);
        $prepare_users    = json_decode(redis()->get($room_prepare_key),true);

        $chairs_arr = [];
        $cache_arr = json_decode(redis()->get($room_chair_key),true);
        foreach ($prepare_users as $v){
            $chairs_arr[] = $cache_arr[$v];
        }
        return $chairs_arr;
    }

    /**
     * 世界广播
     * @param $serv
     * @param $data
     */
    protected function worldBroadcast($serv,$fds){
        $hallData = $this->hallData();
        $data = Packet::packFormat('OK', 1, $hallData);
        $data = Packet::packEncode($data, MainCmd::CMD_SYS, SubCmd::SUB_GAME_HALL_RESP);
        foreach($serv->connections as $fd) {
            if(!isset($fds[$fd])){
                $client_info = $serv->getClientInfo($fd);
                $client[$fd] = $client_info;
                if(isset($client_info['websocket_status']) && $client_info['websocket_status'] == 3) {
                    $serv->push($fd, $data, WEBSOCKET_OPCODE_BINARY);
                }
            }
        }

    }

    /**
     * 房间广播
     * @param $serv
     * @param $room_data
     * @param $fds
     */
    protected function roomBroadcast($serv,$room_data,$fds){
        $user_room_data = Packet::packFormat('OK', 1, $room_data);
        $return_data    = Packet::packEncode($user_room_data, MainCmd::CMD_SYS, SubCmd::SUB_GAME_WAIT);
        foreach($fds as $fd) {
            //获取客户端信息
            $client_info = $serv->getClientInfo($fd);
            $client[$fd] = $client_info;
            if(isset($client_info['websocket_status']) && $client_info['websocket_status'] == 3) {
                $serv->push($fd, $return_data, WEBSOCKET_OPCODE_BINARY);
            }
        }
    }

    /**
     * 房间广播
     * @param $serv
     * @param $fds
     * @param $data
     */
    protected function roomPrepareBroadcast($serv,$fds,$data){
        $user_room_info = Packet::packFormat('OK', 1, $data);
        $return_data    = Packet::packEncode($user_room_info, MainCmd::CMD_SYS, SubCmd::SUB_GAME_PREPARE_RESP);
        foreach($fds as $fd) {
            //获取客户端信息
            $client_info = $serv->getClientInfo($fd);
            $client[$fd] = $client_info;
            if(isset($client_info['websocket_status']) && $client_info['websocket_status'] == 3) {
                $serv->push($fd, $return_data, WEBSOCKET_OPCODE_BINARY);
            }
        }
    }

    /**
     * 游戏大厅房间数据
     * @return array
     */
    protected function hallData(){
        $game_conf = config('game');
        $room_total_key = $game_conf['user_room_total'];
        $fake_room_data = $this->fakeHallData();
        if(redis()->exists($room_total_key)){
            $rooms = redis()->sMembers($room_total_key);
            $hall_rooms = array();
            $hall_datas = array();
            $wait_room_conf = $game_conf['user_room_wait'];
            $room_data_conf = $game_conf['user_room_data'];
            foreach($rooms as $val){
                $wait_room_key = sprintf($wait_room_conf,$val);
                $room_data_key = sprintf($room_data_conf,$val);
                // 正在等待的房间
                if(redis()->exists($wait_room_key)){
                    $hall_rooms[$val] = json_decode(redis()->get($wait_room_key),true);
                }
                // 正在游戏的房间
                if(redis()->exists($room_data_key)){
                    $room_data = redis()->hGetAll($room_data_key);
                    if(!isset($room_data['is_game_over']) || ($room_data['is_game_over'] == 0)){
                        if(!empty($room_data['uinfo'])){
                            $wait_users = [];   // 正在等待房间里的用户account
                            $room_data_uinfo = json_decode($room_data['uinfo'],true);
                            foreach ($room_data_uinfo as $ks=>$vs){
                                $wait_users[] = $ks;
                            }
                            $hall_rooms[$val] = $wait_users;
                        }
                    }
                }
            }
            if(!empty($hall_rooms)){
                // 查询个房号用户信息
                $i = 0;
                foreach($hall_rooms as $k=>$v){
                    if(!empty($v)){
                        // 房间用户存在，查询用户信息
                        $curr_room_users = User::getUsers($v);
                        if($curr_room_users['code'] == 0){
                            $curr_room_data = $curr_room_users['data'];
                            foreach ($curr_room_data as $ks=>$vs){
                                $curr_room_data[$ks]['chair_id'] = $this->findChairId($k,$vs['account']);
                            }
                            $hall_datas[$i]['room_no']   = $k;
                            $hall_datas[$i]['room_data'] = $curr_room_data;
                        }
                        $i++;
                    }
                }
                $total_room_data = array_merge($fake_room_data,$hall_datas);
                return $total_room_data;
            }else{
                return $fake_room_data;
            }
        }else{
            return $fake_room_data;
        }
    }

    /**
     * 返回玩家房间桌位号
     * @param $room_no
     * @param $account
     * @return mixed
     */
    protected function findChairId($room_no,$account){
        $game_conf = config('game');
        $room_chair_key = sprintf($game_conf['user_room_chair'],$room_no);
        $chair_data = json_decode(redis()->get($room_chair_key),true);
        return $chair_data[$account];
    }

    /**
     * 模拟大厅数据
     */
    protected function fakeHallData(){
        $game_conf = config('game');
        $fake_room_key = $game_conf['fake_room_hallFake'];
        $fake_expire = $game_conf['fake_expire'];
        if(redis()->exists($fake_room_key)){
            $fake_room_data = json_decode(redis()->get($fake_room_key),true);
            return $fake_room_data;
        }else{
            $num = rand(10000,99999);
            $data = User::getUserNum($num);
            $fake_room_data = [];
            $team_arr = array_chunk($data,3);
            $room_no = 9989;
            foreach($team_arr as $k=>$v){
                $team_data = [];
                if(!empty($v)){
                    foreach ($v as $kk=>$vv){
                        $team_data[] = [
                            'account'  => $vv['account'],
                            'nickname' => $this->filterNickname($vv['nickname']),
                            'score'    => $vv['score'],
                            'avatar'   => $vv['avatar'],
                            'chair_id' => (int)($kk+1),
                        ];
                    }
                }
                $fake_room_data[] = ['room_data'=>$team_data,'room_no'=>(int)($room_no+$k)];
            }
            // 缓存模拟房间数据
            redis()->set($fake_room_key,json_encode($fake_room_data,JSON_UNESCAPED_SLASHES));
            redis()->expire($fake_room_key,$fake_expire);
            return $fake_room_data;
        }
    }

    /**
     * 去除昵称空格特殊字符和emoji
     * @param $account
     * @return null|string|string[]
     */
    protected function filterNickname($account){
        $regex = "/\/|\~|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\+|\{|\}|\:|\<|\>|\?|\[|\]|\,|\.|\/|\;|\'|\`|\-|\=|\\\|\|[\x{1F600}-\x{1F64F}]|[\x{1F300}-\x{1F5FF}]|[\x{1F680}-\x{1F6FF}]|[\x{2600}-\x{26FF}]|[\x{2700}-\x{27BF}]|/";
        $str = preg_replace($regex,'',$account);
        return preg_replace('# #', '', $str);
    }
}